//-----------------------------------------------------------------------------
//
// (c) Copyright 2012-2013 Xilinx, Inc. All rights reserved.
//
// This file contains confidential and proprietary information of Xilinx, Inc.
// and is protected under U.S. and international copyright and other
// intellectual property laws.
//
// DISCLAIMER
//
// This disclaimer is not a license and does not grant any rights to the
// materials distributed herewith. Except as otherwise provided in a valid
// license issued to you by Xilinx, and to the maximum extent permitted by
// applicable law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND WITH ALL
// FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS,
// IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
// MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE;
// and (2) Xilinx shall not be liable (whether in contract or tort, including
// negligence, or under any other theory of liability) for any loss or damage
// of any kind or nature related to, arising under or in connection with these
// materials, including for any direct, or any indirect, special, incidental,
// or consequential loss or damage (including loss of data, profits, goodwill,
// or any type of loss or damage suffered as a result of any action brought by
// a third party) even if such damage or loss was reasonably foreseeable or
// Xilinx had been advised of the possibility of the same.
//
// CRITICAL APPLICATIONS
//
// Xilinx products are not designed or intended to be fail-safe, or for use in
// any application requiring fail-safe performance, such as life-support or
// safety devices or systems, Class III medical devices, nuclear facilities,
// applications related to the deployment of airbags, or any other
// applications that could lead to death, personal injury, or severe property
// or environmental damage (individually and collectively, "Critical
// Applications"). Customer assumes the sole risk and liability of any use of
// Xilinx products in Critical Applications, subject only to applicable laws
// and regulations governing limitations on product liability.
//
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE
// AT ALL TIMES.

// This module loops back AXI ethernet RX traffic and transmits it back on 
// AXI ethernet TX ports. This mode is enabled if an external ethernet 
// traffic generator drives the AXI ethernet RX ports.
// enable_loopback - Allowed values (0,1); disable/enable external traffic generator 

// Packet format of the ethernet packets generated by this block
//  -------------------------------------------------------------------------
//  | byte 8 | byte 7 | byte 6 | byte 5 | byte 4 | byte 3 | byte 2 | byte 1 |
//  -------------------------------------------------------------------------
//  | Source Address  |           Destination Address                       |
//  -------------------------------------------------------------------------
//  |  rx_axis_tdata  |  rx_axis_tdata  |       Source Address              |
//  -------------------------------------------------------------------------
//  |  ...............................  |  rx_axis_tdata  |  rx_axis_tdata  |
//  -------------------------------------------------------------------------
//  |  rx_axis_tdata  | ................................................... |
//  -------------------------------------------------------------------------

// The source and destination addresses are swapped out to XIL_MAC_ID_THIS
// and EXT_MAC_ID which are mac IDs associated with this traffic generator
// Assume this traffic generator is TG0

//               --------        TX         --------
//         ----->|      |------------------>|      |
//         |  TG0| MAC1 |                   | MAC3 |
//         |     |      |                   |      |
//         |     --------                   --------
//         |
//         |
//         |     --------        RX         --------
//         ------|      |<------------------|      |
//               | MAC2 |               ETG1| MAC4 |
//               |      |                   |      |
//               --------                   --------

// On the RX traffic the 
// src addr = mac ID of MAC4 connected to external traffic generator ETG1
// dst addr = mac ID of MAC2
//
// The data received by MAC2 AXI RX ports is looped back on MAC1 AXI TX ports
// So on the TX traffic the 
// src addr = mac ID of MAC1 connected to traffic generator ETG0
// dst addr = mac ID of MAC3

`timescale 1ps / 1ps

module axi_stream_lb #(
    parameter AXIS_TDATA_WIDTH =  64,
    parameter AXIS_TKEEP_WIDTH =  16,   
    parameter XIL_MAC_ID_THIS    =  48'h111100000000, // 00:00:00:00:11:11
    parameter EXT_MAC_ID         =  48'h333300000000  // 00:00:00:00:33:33
)
(
    input                         reset,   
    input                         tx_axis_clk,

    output [AXIS_TDATA_WIDTH-1:0] tx_axis_tdata,
    output [AXIS_TKEEP_WIDTH-1:0] tx_axis_tkeep,
    output                        tx_axis_tvalid,
    output                        tx_axis_tlast,
    output                        tx_axis_tuser,
    input                         tx_axis_tready,
    
    input  [AXIS_TDATA_WIDTH-1:0] rx_axis_tdata,
    input  [AXIS_TKEEP_WIDTH-1:0] rx_axis_tkeep,
    input                         rx_axis_tvalid,
    input                         rx_axis_tlast,
    input                         rx_axis_tuser,
    output                        rx_axis_tready,

    output reg                    lb_dropped_packet,
    input                         enable_loopback

);

  // Ethernet header is 128 bits, this includes 16 bits of data payload
  // see packet format above
  // Ethernet Header Count defines the number of ethernet header DWORDS
  // 1 DWORD =  AXIS_TDATA_WIDTH bits
  localparam [3:0]   ETH_HEADER_CNT  = 128/AXIS_TDATA_WIDTH;

  wire [127:0]                  ethernet_header;
  reg  [3:0]                    hd_cnt = 0;

  reg  [AXIS_TDATA_WIDTH-1:0]   rx_data_lb = 'b0;
  reg  [AXIS_TKEEP_WIDTH-1:0]   rx_data_keep_lb = 'b0;
  reg                           rx_data_valid_lb = 'b0;
  reg                           rx_data_last_lb = 'b0;

  wire [AXIS_TDATA_WIDTH-1:0]   fifo_rx_axis_tdata;
  wire [AXIS_TKEEP_WIDTH-1:0]   fifo_rx_axis_tkeep;
  wire                          fifo_rx_axis_tvalid;
  wire                          fifo_rx_axis_tlast;
  wire                          fifo_rx_axis_tready;

  // enumerated states
  localparam IDLE      = 1'b0;
  localparam IN_PKT    = 1'b1;
  
  reg  cstate = 1'b0;   // current state
  reg  nstate = 1'b0;   // next state


  always @(posedge tx_axis_clk)
  begin
    // hold value if tready is not asserted
    if (fifo_rx_axis_tready)
    begin
      rx_data_valid_lb <= rx_axis_tvalid;
      rx_data_last_lb  <= rx_axis_tlast; 
      rx_data_keep_lb  <= rx_axis_tkeep; 
    end
  end

  // dst addr -  MAC ID of the mac which receives RX data
  assign ethernet_header [47:0]     = EXT_MAC_ID;   
  // src addr -  MAC ID of the mac connected to this generator
  // which sends TX data
  assign ethernet_header [95:48]    = XIL_MAC_ID_THIS;
  assign ethernet_header [127:96]   = rx_axis_tdata[(AXIS_TDATA_WIDTH-1)-:32];

  always @(posedge tx_axis_clk)
  begin
    // hold value if tready is not asserted
    if (!fifo_rx_axis_tready)
      rx_data_lb <= rx_data_lb;
    // Ethernet header
    else if (cstate == IDLE ) 
      rx_data_lb <=  ethernet_header[AXIS_TDATA_WIDTH-1:0];
    // Ethernet header
    else if (hd_cnt != ETH_HEADER_CNT ) 
      rx_data_lb <=  ethernet_header[(AXIS_TDATA_WIDTH * hd_cnt)+:AXIS_TDATA_WIDTH];
    else  
      rx_data_lb <=  rx_axis_tdata;
  end

  // Current and Next state logic
  always @ (posedge tx_axis_clk) begin
    if (reset == 1'b1)
      cstate <= IDLE;
    else 
      cstate <= nstate;
  end
  

  always@(cstate, enable_loopback, rx_axis_tready, rx_axis_tvalid, rx_axis_tlast) begin
    case (cstate)
      IDLE: begin
        if (enable_loopback && rx_axis_tready && rx_axis_tvalid) begin
          nstate <= IN_PKT;
        end else begin
          nstate <= IDLE;
        end
      end
  
      IN_PKT: begin
        if (rx_axis_tready && rx_axis_tvalid && rx_axis_tlast) begin
          nstate <= IDLE;
        end else begin
          nstate <= IN_PKT;
        end
      end
  
      default : begin
        nstate <= IDLE;
      end
  
    endcase
  end

 // Counting ethernet header DWORDs
 // 1 DWORD =  AXIS_TDATA_WIDTH bits
 always @(posedge tx_axis_clk)
  begin
    // hold value if tready is not asserted
    if (!fifo_rx_axis_tready)
      hd_cnt <= hd_cnt;
    else if (cstate == IDLE) 
      hd_cnt <= 1;
    else if (hd_cnt == ETH_HEADER_CNT) 
      hd_cnt <= hd_cnt;
    else 
      hd_cnt <= hd_cnt + 1;
  end

  // FIFO Data
  assign  fifo_rx_axis_tdata  =  rx_data_lb; 
  assign  fifo_rx_axis_tvalid =  rx_data_valid_lb;
  assign  fifo_rx_axis_tkeep  =  rx_data_keep_lb; 
  assign  fifo_rx_axis_tlast  =  rx_data_last_lb; 
 
  assign  rx_axis_tready = fifo_rx_axis_tready;

axis_data_fifo_0 axis_data_fifo_0_inst
(
  .s_axis_aresetn        (!reset              ),   //input 
  .s_axis_aclk           (tx_axis_clk         ),   //input 
  .s_axis_tready         (fifo_rx_axis_tready ),   //output
  .s_axis_tvalid         (fifo_rx_axis_tvalid ),   //input 
  .s_axis_tdata          (fifo_rx_axis_tdata  ),   //input 
  .s_axis_tkeep          (fifo_rx_axis_tkeep  ),   //input 
  .s_axis_tuser          (1'b0                ),   //input 
  .s_axis_tlast          (fifo_rx_axis_tlast  ),   //input 
  .m_axis_tready         (tx_axis_tready      ),   //input 
  .m_axis_tvalid         (tx_axis_tvalid      ),   //output
  .m_axis_tdata          (tx_axis_tdata       ),   //output
  .m_axis_tkeep          (tx_axis_tkeep       ),   //output
  .m_axis_tuser          (tx_axis_tuser       ),   //output
  .m_axis_tlast          (tx_axis_tlast       ),   //output
  .axis_data_count       (                    ),   //output
  .axis_wr_data_count    (                    ),   //output
  .axis_rd_data_count    (                    )    //output
  );
 

  always @(posedge tx_axis_clk)
  begin
    if (reset)
      lb_dropped_packet <= 1'b0;      
    else if (!rx_axis_tready & rx_axis_tvalid & tx_axis_tvalid)
      lb_dropped_packet <= 1'b1;
  end

endmodule
